﻿using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using Vision.Utilities.VisualStudioTool.Model;
using WeifenLuo.WinFormsUI.Docking;

namespace Vision.Utilities.VisualStudioTool
{
    public partial class KPIForm : DockContent
    {
        const string StageFieldReferenceName = "Vision.Stage";
        const string SeverityFieldReferenceName = "Microsoft.VSTS.Common.Severity";
        const string IterationPathFieldReferenceName = "System.IterationPath";
        const string ReleasePrefix = "Release ";
        const string TBDIterationName = "TBD"; 
        

        #region Ctor
        public KPIForm()
        {
            InitializeComponent();
        }
        #endregion

        #region Load
        private void KPIForm_Load(object sender, EventArgs e)
        {
            KPIBugDataGridView.AutoGenerateColumns = false;

            //var tfsUrl = ConfigurationManager.AppSettings["codeChangeTfsDefaultCollectionURL"] ?? "http://andromeda:8080/tfs/CmsCollection";
            //var credential = CredentialCache.DefaultNetworkCredentials;
            //var tfsTeamProjectCollection = new TfsTeamProjectCollection(new Uri(tfsUrl), credential);

            //tfsTeamProjectCollection.EnsureAuthenticated();
            //if (tfsTeamProjectCollection.HasAuthenticated)
            //{
            //    AuthenticatedTfsTeamProjectCollection = tfsTeamProjectCollection; 
            //}
            //else
            //{
            //    AuthenticatedTfsTeamProjectCollection = null;
            //    MessageBox.Show("Authenticated failed.");
            //    return;
            //}

            var workItemStore = Util.AuthenticatedTfsTeamProjectCollection.GetService<WorkItemStore>();
            ProjectComboBox.ValueMember = "ID";
            ProjectComboBox.DisplayMember = "Name";
            ProjectComboBox.DataSource = workItemStore.Projects.OfType<Project>()
                .Select(a => new { 
                a.Name,
                a.Id
            }).ToList();
        }
        #endregion

        private void SelectAllIterationCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            foreach (TreeNode treeNode in IterationTreeView.Nodes)
            {
                treeNode.Checked = SelectAllIterationCheckBox.Checked;
            }
        }

        private void ProjectComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ProjectComboBox.SelectedIndex >= 0)
            {
                IterationTreeView.Nodes.Clear();
                var selectedProjectID = (int)((dynamic)ProjectComboBox.SelectedItem).Id;
                var workItemStore = Util.AuthenticatedTfsTeamProjectCollection.GetService<WorkItemStore>();
                var project = workItemStore.Projects.OfType<Project>().Where(a => a.Id == selectedProjectID).FirstOrDefault();
                if (project != null)
                {
                    var rootNodes = project.IterationRootNodes.OfType<Node>();
                    BindIterationTreeView(IterationTreeView.Nodes, rootNodes, true);
                    if (IterationTreeView.Nodes.Count > 0)
                        IterationTreeView.Nodes[0].EnsureVisible();
                }
            }
        }
         
        private void BindIterationTreeView(TreeNodeCollection treeNodeCollection, IEnumerable<Node> nodes, bool needCheckBox)
        {
            foreach (var node in nodes.OrderByDescending(a=>a.Name))
            {
                if (node.Name.Equals(TBDIterationName, StringComparison.InvariantCultureIgnoreCase))
                    continue;

                var childTreeNode = new TreeNode(node.Name);
                childTreeNode.Tag = node;
                treeNodeCollection.Add(childTreeNode);
                if (!needCheckBox)
                    HideCheckBox(IterationTreeView, childTreeNode);
                if (node.HasChildNodes)
                {
                    BindIterationTreeView(childTreeNode.Nodes, node.ChildNodes.OfType<Node>(), false);
                    childTreeNode.ExpandAll();
                }
            }
        }

        private void IterationTreeView_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (e.Node.Nodes.Count > 0)
            {
            }
        }

        private void KPIBugButton_Click(object sender, EventArgs e)
        {
            var selectedProject = ((dynamic)ProjectComboBox.SelectedItem).Name;
            var selectIterationNodes = GetSelectIterationNodes(IterationTreeView.Nodes.OfType<TreeNode>());
            if (!selectIterationNodes.Any())
            {
                MessageBox.Show("Please select at least 1 iteration!");
                return;
            }

            var workItemStore = Util.AuthenticatedTfsTeamProjectCollection.GetService<WorkItemStore>();
            var commonStructureService = Util.AuthenticatedTfsTeamProjectCollection.GetService<ICommonStructureService4>();

            var queryBuilder = new StringBuilder();
            queryBuilder.Append("Select System.Id, System.Title from workitems where ");
            queryBuilder.AppendFormat(" [System.TeamProject] = '{0}' ", selectedProject);
            queryBuilder.Append(" AND [System.WorkItemType]='Bug' AND NOT(System.Title Contains 'Build Failure') ");

            var bugModelList = new List<KPIBugModel>();
            var iterationInfoModelList = new List<IterationInfoModel>();
            var iterationQueryBuilder = new StringBuilder();
            foreach (var iterationNode in selectIterationNodes)
            {
                var iterationInfo = commonStructureService.GetNode(iterationNode.Uri.ToString());
                var hasStartDate = iterationInfo.StartDate.HasValue;
                var hasFinishDate = iterationInfo.FinishDate.HasValue;
                if (hasStartDate || hasFinishDate)
                {
                    bugModelList.AddRange(
                        Enum.GetValues(typeof(BugSeverity)).Cast<BugSeverity>().Select(a => new KPIBugModel
                    {
                        IterationPath = iterationNode.Path,  // like: CMS 6\Release 2015.2.629.0\Sprint 79
                        Release = FormatRelease(iterationInfo.Name),// iterationInfo.Path like:  \CMS 6\Iterations\Release 2015.2.629.0\Sprint 79
                        Severity = a,
                        NumberOfDevelopment = 0,
                        NumberOfProduct = 0,
                        NumberOfReleaseQA = 0,
                        Total = 0
                    }));

                    if (iterationQueryBuilder.Length > 0)
                        iterationQueryBuilder.Append(" OR ");
                    iterationQueryBuilder.Append("(");
                    if (hasStartDate)
                    {
                        iterationQueryBuilder.AppendFormat("System.CreatedDate>='{0}'", iterationInfo.StartDate);
                        if (hasFinishDate)
                            iterationQueryBuilder.AppendFormat(" AND ");
                    }
                    if (hasFinishDate)
                    {
                        iterationQueryBuilder.AppendFormat("System.CreatedDate<'{0}'", iterationInfo.FinishDate.Value.AddDays(1));
                    }
                    iterationQueryBuilder.Append(")");
                    iterationInfoModelList.Add(new IterationInfoModel
                    {
                        IterationPath = iterationNode.Path,
                        StartDate = iterationInfo.StartDate.Value,
                        FinishDate = iterationInfo.FinishDate.Value
                    });
                }
            }
            queryBuilder.AppendFormat(" AND ({0})", iterationQueryBuilder.ToString());

            var workItemCollection = workItemStore.Query(queryBuilder.ToString());

            foreach (WorkItem item in workItemCollection)
            {
                var stageField = item.Fields[StageFieldReferenceName];
                var stage = (stageField.Value ?? "").ToString();

                var severityField = item.Fields[SeverityFieldReferenceName];
                var severitySegments = (severityField.Value ?? "").ToString().Split('-');
                var severity = BugSeverity.Medium.ToString();
                if (severitySegments.Count() >= 2)
                    severity = severitySegments[1].Trim();

                var createdDate = item.CreatedDate;

                var iterationPathField = item.Fields[IterationPathFieldReferenceName];
                var iterationPath = (iterationPathField.Value ?? "").ToString();

                var iterationInfoModel = iterationInfoModelList.First(a => createdDate >= a.StartDate && createdDate.AddDays(-1) < a.FinishDate);

                var bugModel = bugModelList.First(a => a.Severity.ToString().Equals(severity, StringComparison.InvariantCultureIgnoreCase)
                                                     && a.IterationPath.Equals(iterationInfoModel.IterationPath, StringComparison.InvariantCultureIgnoreCase));

                bugModel.Total++;
                var bugStage = BugStage.Development;
                if (Enum.TryParse<BugStage>(stage, out bugStage))
                {
                    switch (bugStage)
                    {
                        case BugStage.Product:
                            bugModel.NumberOfProduct++;
                            break;
                        case BugStage.ReleaseQA:
                            bugModel.NumberOfReleaseQA++;
                            break;
                        case BugStage.Development:
                        default:
                            bugModel.NumberOfDevelopment++;
                            break;
                    }
                }
                else
                {
                    bugModel.NumberOfReleaseQA++;
                }

            }

            bugModelList = bugModelList.OrderBy(a => a.Release).ToList();

            KPIBugDataGridView.DataSource = bugModelList.OrderByDescending(a => a.Release).ToList();

            BindCharts(bugModelList);
        }

        private void BindCharts(IEnumerable<KPIBugModel> bugModelList)
        {
            BindChart(bugModelList, BugSeverity.Critical, CriticalChart);
            BindChart(bugModelList, BugSeverity.High, HighChart);
            BindChart(bugModelList, BugSeverity.Medium, MediumChart);
            BindChart(bugModelList, BugSeverity.Low, LowChart);
        }

        private void BindChart(IEnumerable<KPIBugModel> bugModelList, BugSeverity severity, Chart chart)
        {
            var severityBugModelList = bugModelList.Where(a => a.Severity == severity).ToList();
            chart.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
            chart.ChartAreas[0].AxisX.Title = "Release";
            chart.ChartAreas[0].AxisX.TitleFont = new Font(chart.ChartAreas[0].AxisX.TitleFont, FontStyle.Bold);

            chart.ChartAreas[0].AxisY.MajorGrid.Enabled = false;
            chart.ChartAreas[0].AxisY.Title = string.Format("{0} Bugs", severity);
            chart.ChartAreas[0].AxisY.TitleFont = new Font(chart.ChartAreas[0].AxisY.TitleFont, FontStyle.Bold);
           
            chart.Series[0].Points.Clear();
            chart.Series[1].Points.Clear();
            chart.Series[2].Points.Clear();

            chart.Series[0].Font = new System.Drawing.Font(chart.Series[0].Font, FontStyle.Bold);
            chart.Series[1].Font = new System.Drawing.Font(chart.Series[1].Font, FontStyle.Bold);
            chart.Series[2].Font = new System.Drawing.Font(chart.Series[2].Font, FontStyle.Bold);

            chart.Series[0].IsValueShownAsLabel =true;
            chart.Series[1].IsValueShownAsLabel = true;
            chart.Series[2].IsValueShownAsLabel =true;

            foreach(var bugModel in severityBugModelList)
            {
                chart.Series["Development"].Points.AddXY(bugModel.Release, bugModel.NumberOfDevelopment);
                //chart.Series[0].Points[chart.Series[0].Points.Count - 1].Label = bugModel.NumberOfDevelopment.ToString();

                chart.Series["Release QA"].Points.AddXY(bugModel.Release, bugModel.NumberOfReleaseQA);
                //chart.Series[1].Points[chart.Series[1].Points.Count - 1].Label = bugModel.NumberOfReleaseQA.ToString();

                chart.Series["Product"].Points.AddXY(bugModel.Release, bugModel.NumberOfProduct);
                //chart.Series[2].Points[chart.Series[2].Points.Count - 1].Label = bugModel.NumberOfDevelopment.ToString();
            }

        }

        private List<Node> GetSelectIterationNodes(IEnumerable<TreeNode> treeNodes)
        {
            var selectedNodes = new List<Node>();

            foreach(var treeNode in treeNodes)
            {
                if(treeNode.Checked)
                {
                    selectedNodes.Add(treeNode.Tag as Node);
                }
                if(treeNode.Nodes.Count >0)
                {
                    selectedNodes.AddRange(GetSelectIterationNodes(treeNode.Nodes.OfType<TreeNode>()));
                }
            }

            return selectedNodes;
        }

        private string FormatRelease(string releaseName)
        {
            if (string.IsNullOrEmpty(releaseName))
                return releaseName;
            if (releaseName.StartsWith(ReleasePrefix))
                return releaseName.Substring(ReleasePrefix.Length).Trim();
            return releaseName;
        }

        internal class IterationInfoModel
        {
            public string IterationPath { get; set; }
            public DateTime StartDate { get; set; }
            public DateTime FinishDate { get; set; }
        }

        #region Interop
        private const int TVIF_STATE = 0x8;
        private const int TVIS_STATEIMAGEMASK = 0xF000;
        private const int TV_FIRST = 0x1100;
        private const int TVM_SETITEM = TV_FIRST + 63;

        [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Auto)]
        private struct TVITEM
        {
            public int mask;
            public IntPtr hItem;
            public int state;
            public int stateMask;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpszText;
            public int cchTextMax;
            public int iImage;
            public int iSelectedImage;
            public int cChildren;
            public IntPtr lParam;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam,
                                                 ref TVITEM lParam);

        /// <summary>
        /// Hides the checkbox for the specified node on a TreeView control.
        /// </summary>
        private void HideCheckBox(TreeView treeView, TreeNode node)
        {
            TVITEM tvi = new TVITEM();
            tvi.hItem = node.Handle;
            tvi.mask = TVIF_STATE;
            tvi.stateMask = TVIS_STATEIMAGEMASK;
            tvi.state = 0;
            SendMessage(treeView.Handle, TVM_SETITEM, IntPtr.Zero, ref tvi);
        }
        #endregion

        
    }
}
